<!-- 图片上传组件 -->
<template>
    <el-upload
        v-model:file-list="fileList"
        list-type="picture-card"
        :class="fileList.length >= props.limit || !props.showUploadBtn ? 'hide' : 'show'"
        :before-upload="handleBeforeUpload"
        :action="props.action"
        :headers="props.headers"
        :data="props.data"
        :name="props.name"
        :on-success="handleSuccessFile"
        :on-error="handleError"
        :accept="props.accept"
        :limit="props.limit"
    >
        <i-ep-plus />
        <template #file="{ file }">
            <div style="width: 100%">
                <img class="el-upload-list__item-thumbnail" :src="file.url" alt="" />
                <span class="el-upload-list__item-actions">
                    <span class="el-upload-list__item-preview" @click="previewImg(file)">
                        <el-icon><zoom-in /></el-icon>
                    </span>
                    <span v-if="props.showDelBtn" class="el-upload-list__item-delete" @click="handleRemove(file)">
                        <el-icon><Delete /></el-icon>
                    </span>
                </span>
            </div>
        </template>
    </el-upload>

    <el-image-viewer
        v-if="viewVisible"
        :zoom-rate="1.2"
        :initial-index="initialIndex"
        :url-list="viewFileList"
        @close="closePreview"
    />
</template>
<script setup lang="ts">
import { UploadRawFile, UploadRequestOptions, UploadUserFile, UploadFile, UploadProps } from 'element-plus'
import FileAPI from '@/api/file'
import { TOKEN_KEY } from '@/enums/CacheEnum'
import { ResultEnum } from '@/enums/ResultEnum'

const emit = defineEmits(['update:modelValue'])

const props = defineProps({
    /**
     * 文件路径集合
     */
    modelValue: {
        type: Array<string>,
        default: () => []
    },
    /**
     * 上传地址
     */
    action: {
        type: String,
        default: FileAPI.uploadUrl
    },
    /**
     * 请求头
     */
    headers: {
        type: Object,
        default: () => {
            return {
                Authorization: localStorage.getItem(TOKEN_KEY)
            }
        }
    },
    /**
     * 请求携带的额外参数
     */
    data: {
        type: Object,
        default: () => {
            return {}
        }
    },
    /**
     * 上传文件的参数名
     */
    name: {
        type: String,
        default: 'file'
    },
    /**
     * 文件上传数量限制
     */
    limit: {
        type: Number,
        default: 10
    },
    /**
     * 是否显示删除按钮
     */
    showDelBtn: {
        type: Boolean,
        default: true
    },
    /**
     * 是否显示上传按钮
     */
    showUploadBtn: {
        type: Boolean,
        default: true
    },
    /**
     * 单张图片最大大小
     */
    uploadMaxSize: {
        type: Number,
        default: 2 * 1024 * 1024
    },
    /**
     * 上传文件类型
     */
    accept: {
        type: String,
        default: 'image/*'
    }
})

const viewVisible = ref(false)
const initialIndex = ref(0)

const fileList = ref([] as UploadUserFile[])
const valFileList = ref([] as string[])
const viewFileList = ref([] as string[])

watch(
    () => props.modelValue,
    (newVal: string[]) => {
        const filePaths = fileList.value.map(file => file.url)
        // 监听modelValue文件集合值未变化时，跳过赋值
        if (
            filePaths.length > 0 &&
            filePaths.length === newVal.length &&
            filePaths.every(x => newVal.some(y => y === x)) &&
            newVal.every(y => filePaths.some(x => x === y))
        ) {
            return
        }

        if (newVal.length <= 0) {
            fileList.value = []
            return
        }

        fileList.value = newVal.map(filePath => {
            return { url: filePath } as UploadUserFile
        })
        valFileList.value = newVal.map(filePath => {
            return filePath
        })
    },
    { immediate: true }
)

/**
 * 上传成功回调
 *
 * @param options
 */
const handleSuccessFile = (response: any, file: UploadFile) => {
    if (response.code === ResultEnum.SUCCESS) {
        ElMessage.success('上传成功')
        valFileList.value.push(response.data.url)
        emit('update:modelValue', valFileList.value)
        return
    } else {
        ElMessage.error(response.message || '上传失败')
    }
}

const handleError = (error: any) => {
    ElMessage.error('上传失败')
}

/**
 * 删除图片
 */
function handleRemove(removeFile: UploadFile) {
    const filePath = removeFile.url
    if (filePath) {
        FileAPI.deleteByPath(filePath).then(() => {
            valFileList.value = valFileList.value.filter(x => x !== filePath)
            // 删除成功回调
            emit('update:modelValue', valFileList.value)
        })
    }
}

/**
 * 限制用户上传文件的格式和大小
 */
function handleBeforeUpload(file: UploadRawFile) {
    if (file.size > props.uploadMaxSize) {
        ElMessage.warning('上传图片不能大于' + Math.trunc(props.uploadMaxSize / 1024 / 1024) + 'M')
        return false
    }
    return true
}

/**
 * 预览图片
 */
const previewImg: UploadProps['onPreview'] = (uploadFile: UploadFile) => {
    viewFileList.value = fileList.value.map(file => file.url!)
    initialIndex.value = fileList.value.findIndex(file => file.url === uploadFile.url)
    viewVisible.value = true
}

/**
 * 关闭预览
 */
const closePreview = () => {
    viewVisible.value = false
}
</script>
<style lang="scss" scoped>
.hide {
    :deep(.el-upload--picture-card) {
        display: none;
    }
}

.show {
    :deep(.el-upload--picture-card) {
        display: flex;
    }
}
</style>
